package com.nstc.sftpExchange.config.sftp;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.extra.ssh.Sftp;
import com.alibaba.fastjson.JSON;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

import java.util.Properties;

@Slf4j
    public class SftpConnectionFactory extends BasePooledObjectFactory<FtpConnection> {

    private final SftpProperties sftpInfo;

    public SftpConnectionFactory(SftpProperties sftpInfo) {
        this.sftpInfo = sftpInfo;
    }

    /**
     * 创建托管对象的方法，必须实现
     *
     * @return FtpConnection
     */
    @Override
    public FtpConnection create() {
        log.info("创建Sftp连接中，连接参数信息{}", JSON.toJSONString(sftpInfo));
        ChannelSftp channelSftp = null;
        FtpConnection sftpConnection;
        Session sshSession = null;
        try {
            JSch jsch = new JSch();
            sshSession = jsch.getSession(sftpInfo.getUsername(), sftpInfo.getHost(), sftpInfo.getPort());
            if (StringUtils.isNotEmpty(sftpInfo.getPassword())) {
                //优先采用密码登录
                sshSession.setPassword(sftpInfo.getPassword());
            } else {
                //如果密码为空，采用私钥免密登录
                if (StringUtils.isEmpty(sftpInfo.getPrivateKeyPath())) {
                    jsch.addIdentity("~/.ssh/id_rsa"); // 私钥文件路径
                } else {
                    jsch.addIdentity(sftpInfo.getPrivateKeyPath()); // 私钥文件路径
                }
            }

            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
//            sshConfig.put("kex", "diffie-hellman-group1-sha1");
//            sshConfig.put("kex", "curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256");
//            sshConfig.put("server_host_key", "ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512");
//            sshConfig.put("PubkeyAcceptedAlgorithms", "ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512");
//
//            // 加密算法配置
//            sshConfig.put("cipher.s2c", "aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc");
//            sshConfig.put("cipher.c2s", "aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc");
//            // MAC算法配置
//            sshConfig.put("mac.s2c", "hmac-sha2-256,hmac-sha1");
//            sshConfig.put("mac.c2s", "hmac-sha2-256,hmac-sha1");

//            sshConfig.put("cipher.s2c", "aes128-cbc");
//            sshConfig.put("cipher.c2s", "aes128-cbc");
//            sshConfig.put("mac.s2c", "hmac-md5");
//            sshConfig.put("mac.c2s", "hmac-md5");

            sshSession.setConfig(sshConfig);
            //兼容老版本的ssh-rsa算法
//            sshSession.setConfig("server_host_key", sshSession.getConfig("server_host_key") + ",ssh-rsa");
//            sshSession.setConfig("PubkeyAcceptedAlgorithms", sshSession.getConfig("PubkeyAcceptedAlgorithms") + ",ssh-rsa");

            if (log.isDebugEnabled()) {
                log.debug("ConnectTimeout:{},ReadTimeout:{}", sftpInfo.getConnectTimeout(), sftpInfo.getReadTimeout());
            }
            // 读超时时间
            sshSession.setTimeout(sftpInfo.getReadTimeout());
            // 连接超时时间
            sshSession.connect(sftpInfo.getConnectTimeout());
            Channel channel = sshSession.openChannel("sftp");
            channel.connect(sftpInfo.getConnectTimeout());
            channelSftp = (ChannelSftp) channel;

            Sftp sftp = new Sftp(channelSftp, CharsetUtil.CHARSET_UTF_8);
            String homeDir = sftp.pwd();
            sftpConnection = new FtpConnection();
            sftpConnection.setSftpClient(sftp);
            sftpConnection.setHomeDir(homeDir);
        } catch (Exception e) {
            if (channelSftp != null) {
                channelSftp.disconnect();
            }
            if (sshSession != null) {
                sshSession.disconnect();
            }
            log.error("创建sftp连接失败", e);
            throw new RuntimeException("get Sftp Connection Failed", e);
        }
        log.info("创建Sftp连接成功");
        return sftpConnection;
    }

    /**
     * 包装托管对象的方法，必须实现
     */
    @Override
    public PooledObject<FtpConnection> wrap(FtpConnection sftpConnection) {
        return new DefaultPooledObject<>(sftpConnection);
    }

    /**
     * 当托管对象归还池时，重置对象的状态(清空缓存)，以便下一次再被借用，必须实现
     */
    @Override
    public void passivateObject(PooledObject<FtpConnection> pooledObject) {

    }

    /**
     * 该方法会被调用，以校验托管对象是否还可用.

     * @param p p
     * @return return
     */
    @Override
    public boolean validateObject(PooledObject<FtpConnection> p) {
        SftpConnValidator sftpConnValidator = SpringUtil.getBean(SftpConnValidator.class);
        return sftpConnValidator.valid(p, sftpInfo.getConnectTimeout());
    }

    /**
     * testWhileIdle设置为true时，资源回收线程，回收资源的时候会调用该方法
     * 非必须实现
     *
     * @param p p
     * @throws Exception Exception
     */
    @Override
    public void activateObject(PooledObject<FtpConnection> p) throws Exception {
        super.activateObject(p);
    }

    /**
     * 销毁资源，当资源移出池时，会调用该方法。用于彻底释放托管对象占用的资源
     * 非必须实现
     * 解决session连接线程无法关闭，内存泄漏的问题
     * 问题原因：hutools的sftp.close()并不会自动关闭session，需要我们手动关闭
     *
     * @param p p
     * @throws Exception Exception
     */
    @Override
    public void destroyObject(PooledObject<FtpConnection> p) throws Exception {
        ChannelSftp sftp = p.getObject().getSftpClient().getClient();
        Session session = null;
        try {
            session = sftp.getSession();
        } catch (JSchException e) {
        }
        if (sftp != null) {
            sftp.disconnect();
        }
        if (session != null) {
            session.disconnect();
        }
    }
}
